package main

import "fmt"

// -----------------定义结构体----------------------
//结构体与Java中的类很像

type OldBoy struct {
	Person
}
type OldBoyPoint struct {
	*Person // 结构体是指针类型
}
type OldGirlPoint struct {
	oldGirl *Person
}
type MyCmdMap struct {
	cmdMap map[string]string // map 引用类型在使用时候也要实例化
}
type Person struct {
	Name string

	Age int

	Gender string

	Grade string
}

// ---------------定义方法: 给结构体绑定构造方法--------------------
type Student struct {
	Name string

	Age int

	Gender string

	Grade string
}

func (stu Student) print() {
	fmt.Println("姓名", stu.Name)
	fmt.Println("年龄", stu.Age)
	fmt.Println("性别", stu.Gender)
	fmt.Println("年级", stu.Grade)
}

// 调用:(&stu).print2()
func (stu *Student) print2() {
	(*stu).Name = "露露"
	stu.Name = "露露"
	fmt.Println(stu.Name)
}

func (s *Student) String() string {
	str := fmt.Sprintf("name=%v, age=%v, Gender=%s, Grade=%s", s.Name, s.Age, s.Gender, s.Grade)
	return str
}
func (s Person) personFun() string {
	return fmt.Sprintf("name=%s", s.Name)
}
func (sm MyCmdMap) optCmdMapToString() string {
	out := ""
	for k, v := range sm.cmdMap {
		out = out + fmt.Sprintf("%s=%s", k, v)
	}
	return out
}

// ------------------初始化结构体----------------------
func init1() {
	// 1 这种方式给成员变量赋值需要全部赋值，不能只给其中部分赋值，并且有序，
	stu1 := Student{"张三", 18, "男", "大一"}
	// 2 这种方式可以部分赋值
	stu2 := Student{
		Name:   "李四",
		Age:    20,
		Gender: "女",
		Grade:  "大三",
	}
	// 3 想要返回结构体
	var stu3 *Student = &Student{"张三", 18, "男", "大一"}
	fmt.Println(stu1)
	fmt.Println(stu2)
	fmt.Println(stu3)
	/* 这种方式等同于 &Student{} == new(Student) 相同效果都返回结构体指针*/
	var s4 *Student = &Student{
		Name:   "李四",
		Age:    20,
		Gender: "女",
		Grade:  "大三",
	}
	fmt.Println(*s4)

	// 打印结构体的值
	stu := Student{"张三", 18, "男", "大一"}
	fmt.Println("学生姓名：", stu.Name)
	fmt.Println("学生年龄：", stu.Age)
	fmt.Println("学生性别：", stu.Gender)
	fmt.Println("学生年级：", stu.Grade)
	// 修改结构体中的值
	stu.Name = "李四"
	stu.Age = 19
	stu.Gender = "女"
	stu.Grade = "大二"
	// 打印修改后的结构体的值
	fmt.Println("-----------------")
	fmt.Println("学生姓名：", stu.Name)
	fmt.Println("学生年龄：", stu.Age)
	fmt.Println("学生性别：", stu.Gender)
	fmt.Println("学生年级：", stu.Grade)

	// 调用结构体的方法
	stu.print()
	(&stu).print2()

	stud := Student{
		Name: "lili",
		Age:  20,
	}
	fmt.Println(&stud) // 传入地址，绑定了String方法就会自动调用

}

// --------------------定义结构体指针---------------------------
func structPoint() {
	// 声明结构体并赋值
	stu := Student{"张三", 18, "男", "大一"}
	// 定义结构体指针
	var stuPointer *Student
	// 将结构体地址赋值给指针
	stuPointer = &stu
	// 结构体指针通过点（.）操作符访问成员变量
	fmt.Println("姓名：", stuPointer.Name)
	fmt.Println("年龄：", stuPointer.Age)
	fmt.Println("性别：", stuPointer.Gender)
	fmt.Println("年级：", stuPointer.Grade)

	var t *Student = new(Student)
	(*t).Name = "姗姗"
	(*t).Age = 20
	t.Gender = "女" // 指针类型可以这样直接赋值
	fmt.Println(t)

}

// --------------------结构体类型转换---------------------------
type Stu Student // 类型重定义
func structConvert() {
	stu1 := Student{"张三", 18, "男", "大一"}
	per1 := Person{"张三", 18, "男", "大一"}
	stu1 = Student(per1) // Person类型转为student类型
	fmt.Println(stu1)

	var s1 Student
	var s2 Stu
	s1 = Student(s2)
	fmt.Println(s1)
}

// Stu类型的成员方法
func (s *Stu) Distribute() {
	fmt.Println("发作业")
}

type FooTestFuncRedefine func() // 类型重定义

func (f *FooTestFuncRedefine) test111() {

}

// *******************方法和函数的区别*****************************
/**
函数可以直接调用，方法的调用必须绑定结构体
*/

// *******************跨包访问结构体*****************************
/*
import "service/user/model"
func main()  {
	s:=model.Student{}
}

如果要访问其他包下的小写的结构体：工厂模式, 调用NewStudent方法返回student结构体的指针
func NewStudent(n string, a int) *student {
	return &student{n,a}
}
*/

// *******************同理：go中的封装就是提供GetAge方法*****************************

func OldBoyFunc() {
	oB := OldBoy{}
	oB.Name = "old boy"
	oB.Age = 35
	oB.Grade = "男"
	oB.personFun() // OldBoy结构体就继承了Person 结构体的所有成员变量和成员方法
}
func oldBoyPointFunc() {
	oB := OldBoyPoint{&Person{}} // 如果继承的结构体是指针类型，要实例化内部指针对象, 如果不实例化会空指针异常
	//oB = OldBoyPoint{} // 结构体肚子里是指针类型，必须实例化
	oB.personFun()
	oB.Name = "old boy point"
	oG := OldGirlPoint{oldGirl: &Person{}}
	oG.oldGirl.personFun()

}
func optCmdMao() {
	cm := MyCmdMap{cmdMap: map[string]string{}} // map 类型在使用时候也要实例化
	cm.cmdMap["aa"] = "AA"
}

/**********************************结构体构造方法***********************************/
type Object struct {
	name string
	age  int8
}

// Object结构体的无参构造方法
func NewObject() *Object {
	return &Object{}
}

// 半参构造方法
func NewObject1(name string) *Object {
	return &Object{name: name}
}

// 全参构造方法
func NewObject2(name string, age int8) *Object {
	return &Object{name: name, age: age}
}

// SetAge设置p的年龄: 指针类型接收者
func (p *Object) SetAge(newAge int8) {
	p.age = newAge
}

// 成员方法: 值类型接收者
func (o Object) Dream() {
	fmt.Println("%s的梦想是在%d岁的时候学好go语言", o.name, o.age)
}

// 普通方法
func testObject() {
	o := NewObject2("小明", 10)
	o.Dream()
	o.SetAge(18)
}
